package net.sf.rose.jdbc.database;

import java.sql.PreparedStatement;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import net.sf.rose.boot.BootStrap;
import net.sf.rose.boot.bean.DataTypeBean;
import net.sf.rose.boot.bean.DatabaseBean;
import net.sf.rose.jdbc.bean.FieldBean;
import net.sf.rose.jdbc.bean.TableBean;
import net.sf.rose.jdbc.service.Service;

/**
 * 类说明:数据库表结构元数据操作的封装类
 * 
 * @author fengjian
 * @since 2016/04/20
 */
public abstract class Jdbc {

	/**
	 * 功能:返回本数据库所有的表与视图
	 * 
	 * @param service 数据库连接
	 * @return 返回所有的表与视图
	 * @throws SQLException 数据库操作异常
	 */
	public abstract List<TableBean> getAllDbTables(Service service) throws SQLException;

	/**
	 * 功能:与现有的数据库比较，生成数据库表定义或更新SQL语句
	 * 
	 * @param service 数据库连接
	 * @param table   数据库表定义
	 * @return 返回SQL语句列表
	 * @throws SQLException 数据库操作异常
	 */
	public abstract String getUpdateSQL(Service service, TableBean table) throws SQLException;

	/**
	 * 功能:根据提供的数据库表描述，生成数据库表定义SQL语句
	 * 
	 * @param table 数据库表定义
	 * @return 返回SQL语句列表 数据库操作异常
	 */
	public abstract String getSQL(TableBean table);

	/**
	 * 功能:根据提供的数据库表描述，生成数据库表结构修改SQL语句
	 * 
	 * @param table 新的数据库表定义
	 * @param bak   旧的数据库表定义
	 * @return 返回SQL语句列表
	 */
	public abstract String getSQL(TableBean table, TableBean bak);

	/**
	 * 功能:获取现有数据库表结构的定义
	 * 
	 * @param service      数据库连接
	 * @param databaseName 数据库名称
	 * @param sql          数据库操作语句
	 * @return 表或视图的所有定义
	 * @throws Exception 数据库操作异常
	 */
	public List<FieldBean> getDatabaseTable(Service service, String databaseName, String sql) throws Exception {
		service.setCurrentDatabaseName(databaseName);
		DatabaseBean db = BootStrap.getBootBean().getDatabaseBean(service.getCurrentDatabaseName());
		Map<String, DataTypeBean> types = db.getDataBaseTypeMap();
		List<FieldBean> fields = new ArrayList<>();
		PreparedStatement pstmt = service.getConnection().prepareStatement(sql);
		ResultSetMetaData metaData = pstmt.getMetaData();
		for (int i = 0; i < metaData.getColumnCount(); i++) {
			FieldBean field = new FieldBean();
			field.setFieldName(metaData.getColumnName(i + 1));
			field.setDescription(metaData.getColumnLabel(i + 1));
			field.setDbType(metaData.getColumnTypeName(i + 1));
			DataTypeBean type = types.get(field.getDbType());
			if (type == null) {
				continue;
			}
			field.setType(type.getName());
			field.setJavaType(type.getJavaType());
			field.setPropertyName(field.getFieldName());
			if (FieldBean.isStringType(field)) {
				field.setSize(metaData.getColumnDisplaySize(i + 1));
			} else if (FieldBean.isNumberType(field)) {
				field.setSize(metaData.getPrecision(i + 1));
				field.setScale(metaData.getScale(i + 1));
			}
			int t = metaData.isNullable(i + 1);
			t = t == 0 ? 1 : -1;
			field.setCanNull(t);
			fields.add(field);
		}
		return fields;
	}

}
